package GUI;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.font.FontRenderContext;
import java.awt.font.LineMetrics;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class Graph extends JPanel {
    
	protected String title;
	protected Map<String, ArrayList<Float>> series; 
	protected Map<String, Color> colors;
	
	private float minY;  //used for scaling
	private float maxY;  //used for scaling
	private int maxX;
	
    private static int PAD = 20;
    private static int WIDTH = 300;
    private static int HEIGHT = 300;
    
    private Random rand = new Random();

    public Graph()
    {
    	super();
		super.setPreferredSize(new Dimension(WIDTH,HEIGHT));
		super.setBackground(Color.WHITE);
		series = new HashMap<String, ArrayList<Float>>();
		colors = new HashMap<String, Color>();
    	maxY = 0;
    	minY = 0;
    	maxX = 0;
    	title = "" + this.hashCode();
    }
    
    public Graph(String title)
    {
    	this();
    	this.title = title;
    }
    
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D)g;
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
                            RenderingHints.VALUE_ANTIALIAS_ON);
        int w = getWidth();
        int h = getHeight();
        // Draw Y Axis.
        g2.draw(new Line2D.Double(PAD, PAD, PAD, h-PAD));
        // Draw X Axis.
        g2.draw(new Line2D.Double(PAD, h-PAD, w-PAD, h-PAD));
        // Draw labels.
        Font font = g2.getFont();
        FontRenderContext frc = g2.getFontRenderContext();
        LineMetrics lm = font.getLineMetrics("0", frc);
        float sh = lm.getAscent() + lm.getDescent();
        // X Axis Label.
        String s = "Value";
        float sy = PAD + ((h - 2*PAD) - s.length()*sh)/2 + lm.getAscent();
        for(int i = 0; i < s.length(); i++) {
            String letter = String.valueOf(s.charAt(i));
            float sw = (float)font.getStringBounds(letter, frc).getWidth();
            float sx = (PAD - sw)/2;
            g2.drawString(letter, sx, sy);
            sy += sh;
        }
        // Y Axis Label
        s = "Time";
        sy = h - PAD + (PAD - sh)/2 + lm.getAscent();
        float sw = (float)font.getStringBounds(s, frc).getWidth();
        float sx = (w - sw)/2;
        g2.drawString(s, sx, sy);
        //Draw title
        s = this.title;
        sy = lm.getAscent();
        sw = (float)font.getStringBounds(s, frc).getWidth();
        sx = (w - sw)/2;
        g2.drawString(s, sx, sy);
        //Draw Range
        s = "" + getMin();
        sy = h - PAD + (PAD - sh)/2 + lm.getAscent();
        g2.drawString(s, PAD, sy);
        s = "" + getMax();
        sy = lm.getAscent();
        g2.drawString(s, PAD, sy);
        // Draw lines.
        Iterator<String> iter = series.keySet().iterator();
        while(iter.hasNext())
        {
        	String seriesName = iter.next();
        	ArrayList<Float> values = series.get(seriesName);
        	double xInc = (double)(w - 2*PAD)/(maxX-1);
            double scale = getRange() / (double)(h - 2*PAD);
            //connect points with green line
            g2.setPaint(colors.get(seriesName));
            for(int i = 0; i < values.size()-1; i++) {
                double x1 = PAD + i*xInc;
                double y1 = h - PAD - (1/scale)*(values.get(i) - getMin());
                double x2 = PAD + (i+1)*xInc;
                double y2 = h - PAD - (1/scale)*(values.get(i+1) - getMin());
                g2.draw(new Line2D.Double(x1, y1, x2, y2));
            }
            // Mark data points.
            g2.setPaint(Color.red);
            for(int i = 0; i < values.size(); i++) {
                double x = PAD + i*xInc;
                double y = h - PAD - (1/scale)*(values.get(i) - getMin());
                g2.fill(new Ellipse2D.Double(x-2, y-2, 4, 4));
            }
        }
    }

    private float getMax() 
    {
    	return maxY;
    }
    
    private float getMin()
    {
    	return minY;
    }
    
    private float getRange()
    {
    	return maxY - minY;
    }
    
    public void addPoint(String seriesName, float value)
    {
    	if(series.size() == 0)
    	{
    		minY = value;
    		maxY = value;
    	}
    	else
    	{
    		if(value > maxY)
    			maxY = value;
    		if(value < minY)
    			minY = value;
    	}
    	ArrayList<Float> values = series.get(seriesName);
    	if (values == null)
    	{
    		values = new ArrayList<Float>();
    		series.put(seriesName, values);
    		if(series.size() == 1)
    			colors.put(seriesName, Color.green.darker());	
    		else
    			colors.put(seriesName, new Color(rand.nextFloat(), rand.nextFloat(), rand.nextFloat()));
    	}
    	values.add(value);
    	if(values.size() > maxX)
    		maxX = values.size();
    	repaint();
    }
    
    public void addPoint(float value)
    {
    	addPoint("Series1", value);
    }
    
    public String getTitle()
    {
    	return title;
    }
    
    private void refresh()
    {
    	maxY = 0;
    	minY = 0;
    	series.clear();
    }

    public static void main(String[] args) {
        JFrame f = new JFrame();
        f.setPreferredSize(new Dimension(400, 400));
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        Graph graph = new Graph("Demo Graph");
        graph.addPoint(15.0f);
        graph.addPoint(15.6f);
        graph.addPoint(14.0f);
        graph.addPoint(-1.0f);
        graph.addPoint(8.0f);
        graph.addPoint("Series2", 20);
        graph.addPoint("Series2", 25);
        graph.addPoint("Series2", 29);
        f.add(graph);
        f.setSize(400,400);
        f.setLocation(200,200);
        f.pack();
        f.setVisible(true);
    }
}